package cn.bonoon.kernel.io;

import java.io.UnsupportedEncodingException;

import org.apache.commons.codec.binary.Base64;

import cn.bonoon.kernel.util.StringHelper;
import cn.bonoon.util.PathUtil;

public abstract class AbstractBFile implements BFile{

	/**
	 * 在文件的路径前，以':'分开的部分，这部分可能是用于密码认证的部分数据
	 */
	protected String catalog = "";
	
	/** 
	 * 这个路径一定是文件存储时的相对路径，不能是本地文件存储的绝对路径，所以是可以带有{@link #catalog}的；
	 * 源路径，可能包括文件名或包含{@link #catalog}，是未经过解析的路径，一般来说是无法直接使用的；
	 * 这个路径可能可以直接存储在数据库;
	 * 
	 * 任何在调用到这个字段的时候，都不允许为空
	 * 
	 * 这个有可能是加密过的，加密过的需要进行密钥的认证，认证通过了才会返回文档
	 *  */
	protected String sourcePath = "";

	protected String filename = "";
	
	protected String filesuffix = "";
	
	public AbstractBFile(){}

	public AbstractBFile(AbstractBFile from){
		this.catalog = from.catalog;
		this.sourcePath = from.sourcePath;
		this.filename = from.filename;
		this.filesuffix = from.filesuffix;
	}
	
	public AbstractBFile(BFile from){
		this.catalog = from.getCatalog();
		this.sourcePath = from.getFilepath();
		this.filename = from.getFilename();
		this.filesuffix = from.getFilesuffix();
	}
	
	private String combineFilename(String filename, String filesuffix){
		/*
		 * 这里的文件名认为是已经指定的；
		 * 但有可能指定了文件名，并没有指定文件后缀的情况，
		 * 所以在这里应该判断是否有文件后缀，如果没有文件后缀，没尝试把文件后缀加上去
		 */
		
		if(StringHelper.isEmpty(filesuffix) || filename.endsWith(filesuffix)){ 
			/*
			 * 1.没有指定后缀
			 * 2.指定的后缀与文件名最后一部分一致
			 * 则直接返回文件名
			 */
			return filename;
		}
		
		//把文件名文件后缀组合起来
		if(filesuffix.startsWith(".")) return filename + filesuffix;
		
		return filename + '.' + filesuffix;
	}
	
	/** 如果文件名为空，会尝试从指定的路径上解释文件名的 */
	protected final String internalResolveFilename(){
		if(StringHelper.isNotEmpty(filename)){
			return combineFilename(filename, filesuffix);
		}
		
		/*
		 * 如果文件名为空，则尝试从路径中解释文件名
		 * 如果是从路径中解释文件名，则表示文件名是完整的，应该包含文件的后缀
		 */
		int indexOfFilename = sourcePath.lastIndexOf(PathUtil.separatorChar);
		if(indexOfFilename == -1) return sourcePath;
		
		return sourcePath.substring(indexOfFilename + 1);
	}
	
	/** 尝试从各种位置解释文件的后缀 */
	protected final String interanlResolveFilesuffix(){
		if(StringHelper.isEmpty(filesuffix)){
		
			/*
			 * 指定的文件路径可能是目录，也可能是文件；
			 * 如果是目录，则没有后缀，则需要尝试从文件名里指定
			 * 如何是文件的路径，则可能文件名没有指定；
			 * 所以这里应该优先处理文件路径，尝试从文件路径里查找文件的后缀
			 */
			
			if(StringHelper.isNotEmpty(filename)){
				filesuffix = interanlResolveFilesuffix(filename);
			}else{
				filesuffix = interanlResolveFilesuffix(sourcePath);
			}
		}
		return filesuffix;
	}
	
	private String interanlResolveFilesuffix(String path){
		int indexOfSuffix = path.lastIndexOf('.');
		if(indexOfSuffix > 0){
			String fsuffix = path.substring(indexOfSuffix);
			if(fsuffix.length() < MAX_FILESUFFIX_LENGTH) return fsuffix;
			
			//后缀名大于10位数，明显就是有问题
		}
		return "";
	}
	
	/** 尝试解释指定文件的完整的路径 */
	protected final String internalResolveFilepath(final String sourcePath){
		String fPath = sourcePath;
		
		/*
		 * 路径的任何位置，是允许出现"."的，所以不能认为最后一个位置出现"."就是文件的后缀；
		 * 因为可能还指定了文件名和文件后缀
		 * 
		 * 判断是否指定了文件名，如何指定了，则解释文件名，然后判断路径是否以这个文件名结束；
		 * 如果路径不是以指定的文件名结束的，则组合文件路径和文件名
		 */
		
		if(StringHelper.isNotEmpty(filename)){
			String fName = combineFilename(filename, filesuffix);
			if(!fPath.endsWith(fName)){
				fPath = PathUtil.path(fPath, fName);
			}
		}
		int indexOfCatalog = fPath.lastIndexOf(':');
		if(indexOfCatalog != -1) fPath = fPath.substring(indexOfCatalog + 1);
		return fPath;
	}
	
	protected String internalResolveCatalog(final String sourcePath){
		if(StringHelper.isEmpty(catalog)){
			/*
			 * 如果没有指定，那就尝试从路径里解析出来
			 */
			int indexOfCatalog = sourcePath.lastIndexOf(':');
			if(indexOfCatalog != -1) return sourcePath.substring(0, indexOfCatalog);
		}
		return catalog;
	}
	
	protected String internalResolveFolder(final String sourcePath){
		String fPath = sourcePath;
		
		/*
		 * 如果文件名不为空，则表示"sourcePath"指定的路径里可能不包含文件名；
		 * 这个时候就需要通过解析，把文件名去掉
		 * 如果文件名为空，则表示"sourcePath"路径必须包括文件名
		 */
		
		int filenameOffset = fPath.lastIndexOf(PathUtil.separatorChar);
		if(filenameOffset == -1){
			/*
			 * 这是只有一个目录的情况
			 * 也可能是只有一个文件名的情况
			 * 如果是使用完整路径的话，则必须指定后缀
			 * 
			 * 文件名可以不指定后缀
			 * 没有文件名，只指定了路径，并且这个路径没有包括"/"符，则这个路径必须是文件名
			 */
			if(StringHelper.isEmpty(filename) || fPath.startsWith(filename)){
				//文件直接存储，没有存储在文件夹的情况
				return "";
			}
			//路径就是文件夹
		}else{
		
			if(StringHelper.isNotEmpty(filename)){
				
				/*
				 * 特殊情况，如：path=/f1/f2/fi2.v11, filename=fi2.v11, suffix=.txt
				 * path是目录的情况下
				 */
				
				String fName = combineFilename(filename, filesuffix);
				if(fPath.endsWith(fName)){
					fPath = fPath.substring(0, filenameOffset);
				}
			}else{
				fPath = fPath.substring(0, filenameOffset);
			}
		}
		int indexOfCatalog = fPath.lastIndexOf(':');
		if(indexOfCatalog != -1) fPath = fPath.substring(indexOfCatalog + 1);
		return fPath;
	}
	
	public AbstractBFile(String path, String name, String ext){
		this.sourcePath = path;
		this.filename = name;
		this.filesuffix = ext;
	}
	
	public AbstractBFile(String fullPath){
		/*
		 * 有可能是经过加密的
		 */
		this.sourcePath = new String(Base64.decodeBase64(fullPath));
	}
	
	/** 对文件名进行编码 */	
	public String encodeFilename() throws UnsupportedEncodingException{
		return encodeFilename(internalResolveFilename());
	}

	public static String encodeFilename(String filename) throws UnsupportedEncodingException{
		return new String(filename.getBytes("GBK"), "iso8859-1");
	}
	
	@Override
	public String getFolder() {
		return internalResolveFolder(sourcePath);
	}
	
	@Override
	public String getFilename() {
		return internalResolveFilename();
	}
	
	@Override
	public String getFilesuffix() {
		return interanlResolveFilesuffix();
	}
	
	@Override
	public String getFilepath() {
		return internalResolveFilepath(sourcePath);
	}
	
	@Override
	public String getCatalog() {
		return internalResolveCatalog(sourcePath);
	}
	
	@Override
	public String toString() {
		return internalResolveFilepath(sourcePath);
	}
}
